/*
 * Copyright (c) 2015,广州擎天柱网络科技有限公司
 * All rights reserved.
 *
 * RPC协议编码解码
 *
 * By CoL
 */
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LuaInterface;


public class RpcEncoder {

	// 编码
	public static ByteBuffer Encode(SocketConfig socketCfg, int key, LuaTable args)
	{
		SocketFunction socketFun = socketCfg.getSocketFun(key);

		string functionName = socketFun.function_name;//协议 名字
		int argsLen = args.Length; //获取参数长度
		int rpcArgsLen = socketFun.args.Count;//协议参数长度

		Debug.Assert(argsLen == rpcArgsLen, string.Format("协议封包异常：{0} args count error", functionName));//参数个数不正确

		ByteBuffer buff = new ByteBuffer();//二进制流

		buff.WriteShort((short)key);//写入协议号，目前协议号是16位的

		for (int i = 0; i < rpcArgsLen; i++ )
		{
			SocketFunctionArgs argInfo = socketFun.args[i];
			object arg = args[i + 1];//lua从1开始的
			Debug.Assert(arg != null, string.Format("协议封包异常：fun {0} arg {1} is nil", functionName, i + 1));
			
			if (argInfo.class_index != -1) {
				// 类/结构体
				PackStruct(socketCfg, buff, (LuaTable)arg, argInfo.class_index, argInfo.type);
			} else {
				// 基础类型
				PackBaseType(buff, arg, argInfo.type);
			}
		}
		return buff;
	}

    private static void PackStruct(SocketConfig socketCfg, ByteBuffer buff, LuaTable value, int index, int btype)
	{
		int type = (btype < SocketConfig.ARRAY_VAR) ? btype : btype - SocketConfig.ARRAY_VAR;
		bool isArray = btype >= SocketConfig.ARRAY_VAR;

		SocketClass structs = socketCfg.getSocketClass(index);
		List<SocketClassField> fieldList = structs.field;
		//int fieldLens = fieldList.Count;

		if (isArray)//这里如果是数组，才要封装多一层
		{
			int arrayCount = value.Length;
			buff.WriteInt(arrayCount);
			for (int i = 0; i < arrayCount;i++ )
			{
                PackStructItem(socketCfg, buff, fieldList, (LuaTable)value[i + 1]);
			}
		}else
		{
            PackStructItem(socketCfg, buff, fieldList, value);
		}
	}

    private static void PackStructItem(SocketConfig socketCfg, ByteBuffer buff, List<SocketClassField> fieldList, LuaTable value)
	{
		int fieldLens = fieldList.Count;
		for (int j = 0; j < fieldLens; j++)
		{
			SocketClassField fieldInfo = fieldList[j];
			string fieldName = fieldInfo.field_name;
			object fieldValue;
			if (value[j + 1] != null)//lua从1开始的啊
				fieldValue = value[j + 1];
			else
				fieldValue = value[fieldName];
			if (fieldInfo.class_index != -1) {
				PackStruct (socketCfg, buff, (LuaTable)fieldValue, fieldInfo.class_index, fieldInfo.type);
			} else {
				PackBaseType (buff, fieldValue, fieldInfo.type);
			}
		}
	}

    private static void PackBaseType(ByteBuffer buff, object value, int btype)
	{
		int type = (btype < SocketConfig.ARRAY_VAR) ? btype : btype - SocketConfig.ARRAY_VAR;
		bool isArray = btype >= SocketConfig.ARRAY_VAR;

		if (isArray)//如果是数组
		{
			LuaTable table = (LuaTable)value;
			int valueLen = table.Length;
			buff.WriteInt(valueLen);//写入长度
			for(int i = 0;i<valueLen;i++)
			{
				PackBaseValue(buff, type, table[i + 1]);//lua从1开始
			}
		}else
		{
			PackBaseValue(buff, type, value);
		}
	}

    private static void PackBaseValue(ByteBuffer buff, int btype, object value)
	{
		switch(btype)
		{
		case SocketConfig.TYPE_INT: 
			buff.WriteInt(Convert.ToInt32(value));
			break;
		case SocketConfig.TYPE_STRING: 
			buff.WriteString(Convert.ToString (value));
			break;
		case SocketConfig.TYPE_BUFF: 
			buff.WriteString (Convert.ToString (value));
			break;
		case SocketConfig.TYPE_DOUBLE: 
			buff.WriteDouble(Convert.ToDouble(value));
			break;
		case SocketConfig.TYPE_INT8: 
			buff.WriteByte(Convert.ToSByte(value));
			break;
		case SocketConfig.TYPE_INT16: 
			buff.WriteShort(Convert.ToInt16(value));
			break;
		case SocketConfig.TYPE_FLOAT: 
			buff.WriteFloat(Convert.ToSingle(value));
			break;
		default:
			Debug.Assert(false, "协议封包异常：pack base type args unsupport"); //不支持的输出类型
			break;
		}
	}

	// ---------------------------------------------------------------------

	// 解码
	public static void Decode(SocketConfig socketCfg, int key, ByteBuffer value)
	{
		SocketFunction socketFun = socketCfg.getSocketFun(key);
  		if (socketFun == null)
  		{
  			Debugger.LogWarning("协议解包异常：receive unknown packet:" + key);
  			return;
  		}else{
  			//Debug.Log("ParseValue: " + socketFun.function_name);
  		}

  		ByteBuffer buff = value;	// 是否需要副本?

  		LuaState luaState = LuaEngine.GetMainState();
  		int oldTop = luaState.LuaGetTop();//获取当前的堆栈位置

  		try
  		{
  			luaState.LuaGetGlobal("OnSocket");//获取方法名
  			LuaPush(socketFun.function_name);
  			LuaCreateTable();//封装的table
  			
  			int argsLens = socketFun.args.Count;
  			
  			for (int i = 0; i < argsLens; i++)
  			{
  				LuaPush((i + 1));//push进去key,lua中的key从1开始
  				SocketFunctionArgs argInfo = socketFun.args[i];
  				if (argInfo.class_index != -1)
  				{
  					UnPackStruct(socketCfg, buff, argInfo.class_index, argInfo.type);
  				}
  				else
  					UnPackBaseType(socketCfg, buff, argInfo.type);
  			}
  			
  			luaState.PCall(2, oldTop);//两个参数，一个协议号，一个table
  			luaState.LuaSetTop(oldTop);//重置堆栈位置
  		}catch(Exception e)
  		{
  			luaState.LuaSetTop(oldTop);//重置堆栈位置
            Debugger.Log("协议解包异常：" + e.ToString());
  			//GameManager.Instance.AsyncActionScheduler.EnqueueAction (ThreadSafeLogger.ThreadSafeLog, "协议解包异常：" + e.ToString(), ThreadSafeLogType.Error);
  		}
		buff.Close();
	}

    private static void UnPackStruct(SocketConfig socketCfg, ByteBuffer buff, int index, int btype)
	{
		int type = (btype < SocketConfig.ARRAY_VAR) ? btype : btype - SocketConfig.ARRAY_VAR;
  		bool isArray = btype >= SocketConfig.ARRAY_VAR;
  		int arrayCount = 0;
  		if (isArray) //如果是数组类型
			arrayCount = buff.ReadInt();
  		else
  			arrayCount = 1;//默认为1
		
  		SocketClass structs = socketCfg.getSocketClass(index);
  		List<SocketClassField> fieldList = structs.field;
  		//int fieldLens = fieldList.Count;
		
  		if (isArray)//这里如果是数组，才要封装多一层
  		{
  			LuaCreateTable();
  			for (int i = 0; i < arrayCount;i++ )
  			{
  				LuaPush(i + 1);
                UnPackStructItem(socketCfg, buff, fieldList);
  			}
  			LuaSetTable();//如果是数组才需要设置堆栈
  		}else
	  	{
            UnPackStructItem(socketCfg, buff, fieldList);
  		}
	}

    private static void UnPackStructItem(SocketConfig socketCfg, ByteBuffer buff, List<SocketClassField> fieldList)
	{
		LuaCreateTable();
		int fieldLens = fieldList.Count;
		for (int j = 0; j < fieldLens; j++)
		{
			SocketClassField fieldInfo = fieldList[j];
			LuaPush(fieldInfo.field_name);
			if (fieldInfo.class_index != -1) {
				UnPackStruct (socketCfg, buff, fieldInfo.class_index, fieldInfo.type);
			} else {
				UnPackBaseType (socketCfg, buff, fieldInfo.type);
			}
		}
		LuaSetTable();
	}

    private static void UnPackBaseType(SocketConfig socketCfg, ByteBuffer buff, int btype)
	{
		int type = (btype < SocketConfig.ARRAY_VAR) ? btype : btype - SocketConfig.ARRAY_VAR;
		bool isArray = btype >= SocketConfig.ARRAY_VAR;
		int arrayCount = 0;
		if (isArray) //如果是数组类型
			arrayCount = buff.ReadInt();
		else
			arrayCount = 1;//默认为1
		
		if (isArray)
		{
			LuaCreateTable();
			for (int i = 0; i < arrayCount;i++ )
			{
				LuaPush(i + 1);
				UnPackBaseValue(buff, type);
				LuaSetTable();
			}
		} else {
			UnPackBaseValue(buff, type);
		}
		LuaSetTable();
	}

    private static void UnPackBaseValue(ByteBuffer buff, int btype)
	{
		object data = null;
		switch(btype)
		{
		case SocketConfig.TYPE_INT: 
			data = buff.ReadInt();
			break;
		case SocketConfig.TYPE_STRING: 
			data = buff.ReadLongString();
			break;
		case SocketConfig.TYPE_BUFF: 
			data = buff.ReadLongString();
			break;
		case SocketConfig.TYPE_DOUBLE: 
			data = buff.ReadDouble();
			break;
		case SocketConfig.TYPE_INT8: 
			data = buff.ReadByte();
			break;
		case SocketConfig.TYPE_INT16: 
			data = buff.ReadShort();
			break;
		case SocketConfig.TYPE_FLOAT: 
			data = buff.ReadFloat();
			break;
		default:
			Debug.Assert(false,"协议解包异常：unsupport data type for parse type=" + btype);
			break;
		}
		LuaPush(data);
	}

    private static void LuaSetTable()
    {
        LuaEngine.GetMainState().LuaSetTable(-3);
    }

    private static void LuaCreateTable()
    {
        LuaEngine.GetMainState().LuaCreateTable();
    }

    private static void LuaPush(object value)
    {
        LuaEngine.GetMainState().Push(value);
    }

}